package ru.stepuk.modeller.generators;

/**
 *
 * @author ivan
 */
public class MersenneTwister implements RandomNumberGenerator {

    private final static int N = 624;
    private final static int M = 397;
    private final static int MATRIX_A = 0X9908B0DF;
    private final static int UPPER_MASK = 0X80000000;
    private final static int LOWER_MASK = 0X7FFFFFFF;
    private int[] buffer = new int[N];
    private int i; // Index
    private int initialValue;

    public MersenneTwister() {
        this(5489); // Default initial value
    }

    public MersenneTwister(int initialValue) {
        this.initialValue = initialValue;
        buffer[0] = initialValue;
        for (i = 1; i < N; i++) {
            buffer[i] = 1812433253 * (buffer[i - 1] ^ (buffer[i - 1] >>> 30)) + i;
        }
    }

    private int nextInt() {
        int y;
        int[] mag01 = {0X0, MATRIX_A};

        if (i == N) {
            int k;

            for (k = 0; k < N - M; k++) {
                y = (buffer[k] & UPPER_MASK) | (buffer[k + 1] & LOWER_MASK);
                buffer[k] = buffer[k + M] ^ (y >>> 1) ^ mag01[y & 0X1];
            }

            for (; k < N - 1; k++) {
                y = (buffer[k] & UPPER_MASK) | (buffer[k + 1] & LOWER_MASK);
                buffer[k] = buffer[k + (M - N)] ^ (y >>> 1) ^ mag01[y & 0x1];
            }

            y = (buffer[N - 1] & UPPER_MASK) | (buffer[0] & LOWER_MASK);
            buffer[N - 1] = buffer[M - 1] ^ (y >>> 1) ^ mag01[y & 0X1];

            i = 0;
        }

        y = buffer[i++];

        y ^= (y >>> 11);
        y ^= (y << 7) & 0X9D2C5680;
        y ^= (y << 15) & 0XEFC60000;
        y ^= (y >>> 18);

        return y;
    }

    public double nextValue() {
        return (double) (nextInt() & 0XFFFFFFFFL) / 4294967296.0;
    }

    public int getInitialValue() {
        return initialValue;
    }
}
